package model.elements;

import model.collisions.CollisionCategory;
import model.elements.attributes.Coordinate2D;
import model.elements.attributes.Damage;
import model.elements.attributes.Lives;
import model.elements.attributes.Speed;
import model.elements.behaviours.Mobile;
import model.elements.behaviours.TakesDamage;
import model.elements.behaviours.UsesWeapon;
import model.elements.behaviours.Weapon;
import model.elements.commands.ElementCommand;
import model.elements.commands.ElementCommandFactory;
import model.elements.factory.ElementsFactory;
import model.elements.states.life.Alive;
import model.elements.states.life.Dead;
import model.elements.states.movement.ElementMovementState;
import model.elements.states.movement.MovingRight;
import model.elements.states.movement.Stopped;
import model.elements.states.weapon.BombEquipped;
import model.elements.states.weapon.ElementWeaponState;
import model.exceptions.CommandFactoryIsNotReady;
import model.exceptions.ModelExceptions;
import model.movement.MovementEngine;

// TODO: Auto-generated Javadoc
/**
 * The Class Character.
 */
public abstract class Character extends Element2D implements TakesDamage, Mobile, UsesWeapon
{
	
	/** The m_speed. */
	protected Speed 					m_speed;			// Velocidad Base
	
	/** The m_movement sate. */
	protected ElementMovementState 		m_movementSate   = new Stopped();			// El estado de movimiento. Determina cual va a ser la proxima posicion posible.
	
	/** The m_lives. */
	protected Lives 					m_lives;			// Vidas que bajan con daño
	
	/** The m_equipped weapon. */
	protected ElementWeaponState		m_equippedWeapon = new BombEquipped(3);	// El arma equipada	}
	
	/** The m_facing to. */
	protected ElementMovementState		m_facingTo 		 = new MovingRight();
	
	/** The m_inmortal turns. */
	private int 						m_inmortalTurns 	= 0;
	
	/** The m_max inmortal turns. */
	private int 						m_maxInmortalTurns = 100;
	
	/** The m_took damage. */
	private boolean 					m_tookDamage	= false;
	
	/**
	 * Instantiates a new character.
	 *
	 * @param position the position
	 * @throws ModelExceptions the model exceptions
	 */
	public Character(Coordinate2D position) throws ModelExceptions
	{
		super(position);	
		ElementCommand command = ElementCommandFactory.getElementDoesNothing();
		m_lifeState = new Alive( command );
	}
	
	
	
	/**
	 * * Positionable Interface **.
	 *
	 * @throws ModelExceptions the model exceptions
	 */
	
	/*
	 // DEPRECATED
	@Override
	public boolean isPlayerCharacter()
	{
		return false;
	}
	
	@Override
	public boolean isNonPlayerCharacter()
	{
		return false;
	}
	
	@Override
	public boolean isMobile()
	{
		return true;
	}
	*/
	
	/******/
	
	/* (non-Javadoc)
	 * @see model.elements.Element2D#update()
	 */
	@Override
	public void update() throws ModelExceptions
	{
		m_lifeState.getCommand().execute( this );
		m_inmortalTurns = m_inmortalTurns +1;
		if(m_inmortalTurns >= m_maxInmortalTurns)
			m_tookDamage = false;
	}
	
	/**
	 * * TakesDamage Interface.
	 *
	 * @param damage the damage
	 * @throws CommandFactoryIsNotReady **
	 */
	
	@Override
	public void takeDamage( Damage damage ) throws CommandFactoryIsNotReady
	{
		if(m_tookDamage && m_inmortalTurns < m_maxInmortalTurns)return;
		
		m_inmortalTurns = 0;
		m_tookDamage = true;
		
		m_lives.setValue( m_lives.getValue() - damage.getDamageValue() );
		
		if ( m_lives.getValue() <= 0 )
			m_lifeState = new Dead();
		
	}

	/* (non-Javadoc)
	 * @see model.elements.Element2D#isAlive()
	 */
	@Override
	public boolean isAlive()
	{		
		return m_lifeState.isAlive();
	}

	/* (non-Javadoc)
	 * @see model.elements.states.life.Mortal#onDeath()
	 */
	@Override
	public void onDeath() throws CommandFactoryIsNotReady, ModelExceptions {}
	
	/**
	 * ***.
	 *
	 * @param position the new position
	 */
	
	/*** Mobile Interface ***/
	
	@Override
	public void setPosition(Coordinate2D position)
	{
		m_currentPosition = position;
	}

	/* (non-Javadoc)
	 * @see model.elements.behaviours.Mobile#getSpeed()
	 */
	@Override
	public Speed getSpeed()
	{
		return m_speed;
	}
	
	/* (non-Javadoc)
	 * @see model.elements.behaviours.Mobile#setMovementState(model.elements.states.movement.ElementMovementState)
	 */
	@Override
	public void setMovementState( ElementMovementState movementState )
	{
		m_facingTo = movementState.getFacingTo(m_movementSate);
		m_movementSate = movementState;
	}

	/**
	 * Gets the movement state.
	 *
	 * @return the movement state
	 */
	public ElementMovementState getMovementState()
	{
		return m_movementSate;
	}

	
	/* (non-Javadoc)
	 * @see model.elements.behaviours.Mobile#getNextPosition()
	 */
	@Override
	public Coordinate2D getNextPosition()
	{
		return MovementEngine.getNextPositionForNonAcceleratedMovement
        (
			m_currentPosition, 
			m_movementSate, 
			m_speed
		);
	}
	
	/* (non-Javadoc)
	 * @see model.elements.behaviours.Mobile#getNextTile()
	 */
	@Override
	public Coordinate2D getNextTile()
	{
		return MovementEngine.getNextPositionForNonAcceleratedMovement
        (
			m_currentPosition, 
			m_movementSate, 
			new Speed(1.0)
		);	
	}
	
	/**
	 * ***.
	 *
	 * @return the collision category
	 */
	
	@Override
	public CollisionCategory getCollisionCategory()
	{
		return m_lifeState.getCategory();
	}

	/**
	 * Gets the lives.
	 *
	 * @return the lives
	 */
	public Lives getLives()
	{
		return m_lives;
	}

	/**
	 * * Usesweapon Interface **.
	 *
	 * @param position the position
	 * @param factory the factory
	 * @return the weapon
	 * @throws ModelExceptions the model exceptions
	 */

	@Override
	public Weapon getWeapon( Coordinate2D position, ElementsFactory factory )  throws ModelExceptions
	{
		m_lifeState.setCommand( ElementCommandFactory.getElementDoesNothing() );
		return m_equippedWeapon.getWeapon( position, factory );
	}
	
	/**
	 * Sets the weapon state.
	 *
	 * @param weapon the new weapon state
	 */
	protected void setWeaponState(ElementWeaponState weapon)
	{
		m_equippedWeapon = weapon;
	}
	
	/**
	 * ***.
	 *
	 * @return the direction
	 */
	
	@Override
	public ElementMovementState getDirection()
	{
		return m_facingTo;
	}
	
	/* (non-Javadoc)
	 * @see model.elements.behaviours.UsesWeapon#canUseWeapon()
	 */
	@Override
	public boolean canUseWeapon()
	{
		return m_equippedWeapon.canUseWeapon(getPosition());
	}
}
